/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.ruoyi.common.compressor;

/**
 *
 * @author wmao
 */
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class XmlCompressor
  implements Compressor
{
  private boolean enabled;
  private boolean removeComments;
  private boolean removeIntertagSpaces;
  protected static final String tempCdataBlock = "%%%COMPRESS~CDATA~{0,number,#}%%%";
  protected static final Pattern cdataPattern = Pattern.compile("<!\\[CDATA\\[.*?\\]\\]>", 34);
  protected static final Pattern commentPattern = Pattern.compile("<!--.*?-->", 34);
  protected static final Pattern intertagPattern = Pattern.compile(">\\s+<", 34);
  protected static final Pattern tagEndSpacePattern = Pattern.compile("(<(?:[^>]+?))(?:\\s+?)(/?>)", 34);
  protected static final Pattern multispacePattern = Pattern.compile("\\s+(?=[^<]*?>)", 34);
  protected static final Pattern tagPropertyPattern = Pattern.compile("(\\s\\w+)\\s*=\\s*(?=[^<]*?>)", 2);
  protected static final Pattern tempCdataPattern = Pattern.compile("%%%COMPRESS~CDATA~(\\d+?)%%%", 34);
 
  public XmlCompressor()
  {
    this.enabled = true;
 
    this.removeComments = true;
    this.removeIntertagSpaces = true;
  }
 
  @Override
  public String compress(String xml)
  {
    if ((!(this.enabled)) || (xml == null) || (xml.length() == 0)) {
      return xml;
    }
 
    List cdataBlocks = new ArrayList();
 
    xml = preserveBlocks(xml, cdataBlocks);
 
    xml = processXml(xml);
 
    xml = returnBlocks(xml, cdataBlocks);
 
    return xml.trim();
  }
 
  protected String preserveBlocks(String xml, List<String> cdataBlocks)
  {
    Matcher matcher = cdataPattern.matcher(xml);
    int index = 0;
    StringBuffer sb = new StringBuffer();
    while (matcher.find()) {
      cdataBlocks.add(matcher.group(0));
      matcher.appendReplacement(sb, MessageFormat.format("%%%COMPRESS~CDATA~{0,number,#}%%%", new Object[] { Integer.valueOf(index++) }));
    }
    matcher.appendTail(sb);
    xml = sb.toString();
 
    return xml;
  }
 
  protected String returnBlocks(String xml, List<String> cdataBlocks)
  {
    Matcher matcher = tempCdataPattern.matcher(xml);
    StringBuffer sb = new StringBuffer();
    while (matcher.find())
      matcher.appendReplacement(sb, Matcher.quoteReplacement((String)cdataBlocks.get(Integer.parseInt(matcher.group(1)))));
 
    matcher.appendTail(sb);
    xml = sb.toString();
 
    return xml;
  }
 
  protected String processXml(String xml)
  {
    xml = removeComments(xml);
 
    xml = removeIntertagSpaces(xml);
 
    xml = removeSpacesInsideTags(xml);
 
    return xml;
  }
 
  protected String removeSpacesInsideTags(String xml)
  {
    xml = multispacePattern.matcher(xml).replaceAll(" ");
 
    xml = tagPropertyPattern.matcher(xml).replaceAll("$1=");
 
    xml = tagEndSpacePattern.matcher(xml).replaceAll("$1$2");
    return xml;
  }
 
  protected String removeIntertagSpaces(String xml)
  {
    if (this.removeIntertagSpaces)
      xml = intertagPattern.matcher(xml).replaceAll("><");
 
    return xml;
  }
 
  protected String removeComments(String xml)
  {
    if (this.removeComments)
      xml = commentPattern.matcher(xml).replaceAll("");
 
    return xml;
  }
 
  public boolean isEnabled()
  {
    return this.enabled;
  }
 
  public void setEnabled(boolean enabled)
  {
    this.enabled = enabled;
  }
 
  public boolean isRemoveComments()
  {
    return this.removeComments;
  }
 
  public void setRemoveComments(boolean removeComments)
  {
    this.removeComments = removeComments;
  }
 
  public boolean isRemoveIntertagSpaces()
  {
    return this.removeIntertagSpaces;
  }
 
  public void setRemoveIntertagSpaces(boolean removeIntertagSpaces)
  {
    this.removeIntertagSpaces = removeIntertagSpaces;
  }
}